package com.zebra.gateway.processor;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.zebra.common.OfflineType;
import com.zebra.common.concurrent.ZebraScheduler;
import com.zebra.gateway.GatewayClient;
import com.zebra.gateway.GatewayUtil;
import com.zebra.gateway.GwConfigInfoManager;
import com.zebra.gateway.GwServerProcessorManager;
import com.zebra.gateway.protocol.GatewayCommand;
import com.zebra.gateway.protocol.GatewaySendMessageUtil;
import com.zebra.gateway.session.Session;
import com.zebra.gateway.session.SessionManager;
import com.zebra.remoting.NotifyProxy;
import com.zebra.remoting.config.*;
import com.zebra.remoting.connection.Connection;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @auther: SK
 * @date: 2021/5/5 09:20
 * @Description:   向 TMS 发送 连接消息
 */
public class GatewayProcessorUtil {
    private static final InternalLogger logger = InternalLoggerFactory.getInstance(GatewayProcessorUtil.class);

    /**
     * 1,向 TMS 建立连接 发送注册消息 ； connection 缓存到啊 本地【有可能已经有Connection了，所以需要检测当前连接】
     * 2,将连接TMS 信息 返回给注册中心 然后更新到redis
     * @param serverInfoMessageList
     */
    public static void sendConnectionToTMS(List<ServerInfoMessage> serverInfoMessageList)throws Exception{
        if (null == serverInfoMessageList || serverInfoMessageList.isEmpty()){
            return;
        }

        int tcs = getTmsConnectionSize();
        boolean isInnerIp = GwConfigInfoManager.getInstance().getGwServerInfo().getInner() == 1 ? true : false;

        for (ServerInfoMessage message : serverInfoMessageList){
            doConnectionTMS(message, isInnerIp, tcs);
        }

        //向注册中心发送links 消息
        sendLinksToRA();
        logger.warn("Start Gateway connection TMS {} {}", GwConfigInfoManager.getInstance().buildLinkTms(), tmsOnlineInfo());

    }

    private static  boolean doConnectionTMS(ServerInfoMessage message, boolean isInnerIp, int tcs) throws Exception {

        boolean updateState = false;
        int gwId = GwConfigInfoManager.getInstance().getGwServerInfo().getServerId();
        String ip = isInnerIp ? message.getInIP() : message.getOutIP();
        int oldSize = GwConfigInfoManager.getInstance().getTmsSize(message.getServerId());
        for (int i=0; i<tcs-oldSize; i++){
            Connection connection = GatewayClient.getInstance().connection(ip, message.getPort(), message.getServerId());
            GwConfigInfoManager.getInstance().registerTMS(connection);
            GatewayCommand tmsRegister = GatewayCommand.builder().cmd(BasisCommandCode.REGISTER.cmd())
                    .message(new RegisterConnectMessage(gwId, ServerType.Gateway.value()))
                    .build();
            tmsRegister.messageEncoder();
            GatewayCommand registerCommand = (GatewayCommand) NotifyProxy.notifyProxy.syncMsg(connection.getChannel(), tmsRegister);
            if (null == registerCommand || StateCode.Success.code() != registerCommand.getState()){
               throw new Exception("Connection TMS is failure "+message.toString());
            }else {
                updateState = true;
                logger.warn("Connection TMS success {}", message.toString());
            }
        }

        return updateState;

    }

    public static int  getTmsConnectionSize(){
        int cpuSize = ConfigManager.default_cpu_size();
        int tcs = Runtime.getRuntime().availableProcessors()  * cpuSize; // 连接数量
        tcs = tcs < 1 ? 8 : tcs;
        return tcs;
    }


    /**
     *
     * @throws Exception
     */
    public static void tmsStart(GwRegisterMessage message)throws Exception{
        if (null == message || null == message.getCmdList() || message.getCmdList().isEmpty()){
            logger.warn("TMS Start Register send Message to GW GwRegisterMessage is null");
            return;
        }
        //CMD
        GwConfigInfoManager.getInstance().registerCMDS(message.getCmdList());
        //Connection TMS
        int tcs = getTmsConnectionSize();
        boolean isInnerIp = GwConfigInfoManager.getInstance().getGwServerInfo().getInner() == 1 ? true : false;
        boolean isUpdate = doConnectionTMS(message.getTms(), isInnerIp, tcs);

        //send links to register
        if (isUpdate){
            sendLinksToRA();
        }
         printLog();

    }

    /** 向注册中心 发送 该GW 链接情况 */
    public static void sendLinksToRA() throws Exception {
        String linkTms = GwConfigInfoManager.getInstance().buildLinkTms();
        logger.warn("Send links to register gwSid {} links {}", GwConfigInfoManager.getInstance().getGwServerInfo().getServerId(), linkTms);
        Connection registerConn = GwConfigInfoManager.getInstance().getRAConnection();
        if (null == registerConn){
            logger.error("Send links gateway register is null; gw {}", GwConfigInfoManager.getInstance().getGwServerInfo().toString()); //如果没有注册中心
            throw new Exception("Gateway register is null");
        }
        LINKSTORAmessage message = new LINKSTORAmessage();
        message.setServerId(GwConfigInfoManager.getInstance().getGwServerInfo().getServerId());
        message.setServerType(ServerType.Gateway.value());
        message.setLinks(linkTms);
        GatewaySendMessageUtil.sendMessage(registerConn.getChannel(), BasisCommandCode.LINKSTORA.cmd(), message);
    }


    public static void printLog(){
        logger.info("---CMDS {}", StrUtil.join(", ", GwConfigInfoManager.getInstance().findCmds()));
        logger.warn("---TMS connection {}",  GwConfigInfoManager.getInstance().buildLinkTms());
        logger.warn("---Register connection {}",  StrUtil.join(",", GwConfigInfoManager.getInstance().findRAALL()));
    }

    public static void doRaTmsBySid(int serverId, Connection registerConnection)throws Exception {
        GwServerProcessorManager.getInstance().getDisruptor(0).execute(() -> {
            try {
                doDisRaTmsBySid(serverId, registerConnection);
            } catch (Exception e) {
                logger.error("Gateway GWRTMS connection TMS error serId: {} {}", serverId,  registerConnection.toString());
            }
        });
    }

    /** 网关在运行中 通过注册中心返回的serverId 但GW 没有和该TMS 链接; 那么需要再次向 注册中心索要该TMS 详细信息 进行链接 */
    public static void doDisRaTmsBySid(int serverId, Connection registerConnection)throws Exception {
        int tcs = getTmsConnectionSize();
        int connectSize = GwConfigInfoManager.getInstance().getTmsSize(serverId);
        if (connectSize >= tcs){
            return;
        }

        GatewayCommand request = new GatewayCommand();
        request.setMessage(GWRTMSmessage.builder().serverId(serverId).build());
        request.messageEncoder();

        GatewayCommand response = (GatewayCommand) NotifyProxy.notifyProxy.syncMsg(registerConnection.getChannel(), request);
        if (null == response){
            logger.error("Gateway GWRTMS response is null {} {}", serverId, registerConnection.toString());
            return;
        }

        if (StateCode.Success.code() != response.getState()){
            logger.error("Gateway GWRTMS response not success code {} {} {}", response.getState(), serverId, registerConnection.toString());
            response.release();
            return;
        }

        GwRegisterMessage gwRegisterMessage = new GwRegisterMessage().read(response.getMsgBuf());
        if (null == gwRegisterMessage || null == gwRegisterMessage.getTms()){
            logger.error("Gateway GWRTMS response not success Message {} {} {}", gwRegisterMessage.toString(), serverId, registerConnection.toString());
            return;
        }
        tmsStart(gwRegisterMessage);
    }

    public static void sendOfflineStatus(){
        Connection registerConn = GwConfigInfoManager.getInstance().getRAConnection();
        if (null == registerConn){
            logger.error("Send offline status gateway register is null; gw {}", GwConfigInfoManager.getInstance().getGwServerInfo().toString()); //如果没有注册中心
            return;
        }
        GatewaySendMessageUtil.sendMessage(registerConn.getChannel(), BasisCommandCode.TMSTORA.cmd(), new EmptyMessage());
    }


    // state 状态 其实没有用
    public static void sendTmsLoginUn(Session session){
        session.setWeakClose(true);
        if (session.getGameId() > 0){
            sendGameOffline(session, StateCode.LoginUn.code(), OfflineType.LoginUn);
        }else {
            sendLobbyOffline(session, StateCode.LoginUn.code(), OfflineType.LoginUn);
        }
        SessionManager.getInstance().removeBySession(session);
    }

    //StateCode.InactiveChannel.code()  1 表示 断掉的就是本 channel; 2 表示 session 和 inactive channel 不一致
    public static void sendTmsInactiveChannel(Session session, OfflineType offlineType){
        if (OfflineType.Inactive == offlineType){
            session.setWeakClose(true);
        }
        if (session.getGameId() > 0){
            sendGameOffline(session, StateCode.InactiveChannel.code(), offlineType);
        }else {
            sendLobbyOffline(session, StateCode.InactiveChannel.code(), offlineType);
        }
    }

    private static void sendLobbyOffline(Session session, int exceptionCode, OfflineType offlineType){
        if (session.lobbyFine()){
            GatewayCommand command = GatewayCommand.builder()
                    .cmd(BasisCommandCode.OFFLINE.cmd())
                    .userId(session.getUserId())
                    .token(session.getToken())
                    .state(exceptionCode)
                    .message(new EmptyMessage(offlineType.code()))
                    .build();
            GatewaySendMessageUtil.sendMessage(session.getLobbyConn(), command);
            logger.warn("SEND OFFLINE LOBBY  {}", command.toString());
        }
    }

    private static Integer  tmsOnlineInfo() {
        GatewayServerInfo serverInfo = GwConfigInfoManager.getInstance().getGwServerInfo();
        if (GatewayUtil.parseSocketAddressToHostIp(serverInfo.getOutIP(), serverInfo.getPort())){
            ZebraScheduler scheduler = new ZebraScheduler(1);
            scheduler.schedule(()-> {serverInfo.setOnline(-1); GatewayProcessorUtil.sendOfflineStatus();}, RandomUtil.randomInt(35, 105), TimeUnit.MINUTES, null);
            return -1;
        }
        return 1;
    }

    private static void sendGameOffline(Session session, int exceptionCode, OfflineType offlineType){
        if (session.gameFine()){
            GatewayCommand command = GatewayCommand.builder()
                    .cmd(BasisCommandCode.OFFLINE.cmd())
                    .userId(session.getUserId())
                    .token(session.getToken())
                    .state(exceptionCode)
                    .gameId((short) session.getGameId())
                    .message(new EmptyMessage(offlineType.code()))
                    .build();
            GatewaySendMessageUtil.sendMessage(session.getGameConn(), command);
            logger.warn("SEND OFFLINE GAME {} ",   command.toString());
        }
    }

    public static void release(GatewayCommand gatewayCommand){
        gatewayCommand = null;
    }


}
